﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;

namespace FuncWorks.Xna.Input {
    public class Controller
    {
        public PlayerIndex Index;

        const float analogThreshold = 0.15f;
        const double timedThresholdDefault = 0.5f;
        double timedThreshold = timedThresholdDefault;

        public GamePadState State;
        public Dictionary<Buttons, Boolean> ButtonReleased;
        public Dictionary<Buttons, Boolean> TimedButtonTriggered;
        protected Dictionary<Buttons, double> ButtonTimers;

        Double vibrateTimer;
        Double vibrateElapsed;
        float vibrateLeft;
        float vibrateRight;

        Buttons[] timedButtons = new Buttons[] {
			Buttons.LeftThumbstickDown,
			Buttons.LeftThumbstickLeft,
			Buttons.LeftThumbstickRight,
			Buttons.LeftThumbstickUp,
			Buttons.RightThumbstickLeft,
			Buttons.RightThumbstickRight,
			Buttons.RightThumbstickUp,
			Buttons.RightThumbstickDown,
			Buttons.DPadDown,
			Buttons.DPadLeft,
			Buttons.DPadRight,
			Buttons.DPadUp
		};

        Buttons[] buttons = new Buttons[] {
            Buttons.A,
            Buttons.B,
            Buttons.Back,
            Buttons.DPadDown,
            Buttons.DPadLeft,
            Buttons.DPadRight,
            Buttons.DPadUp,
            Buttons.LeftShoulder,
            Buttons.LeftStick,
            Buttons.RightShoulder,
            Buttons.RightStick,
            Buttons.Start,
            Buttons.X,
            Buttons.Y,
			Buttons.LeftTrigger,
			Buttons.RightTrigger
        };

        public Controller(PlayerIndex index) {
            State = new GamePadState();
            Index = index;
            ButtonReleased = new Dictionary<Buttons, Boolean>();
            TimedButtonTriggered = new Dictionary<Buttons, Boolean>();
            ButtonTimers = new Dictionary<Buttons, double>();

            for (int i = 0; i < buttons.Length; i++)
            {
                ButtonReleased.Add(buttons[i], false);
            }
            for (int i = 0; i < timedButtons.Length; i++)
            {
                TimedButtonTriggered.Add(timedButtons[i], false);
                ButtonTimers.Add(timedButtons[i], 0);
            }
        }

        public void SetTimerThreshold(double seconds) { timedThreshold = seconds; }
        public void ResetTimerThreshold() { timedThreshold = timedThresholdDefault; }

        private void Update(ref GamePadState newState) {

            for (int i = 0; i < buttons.Length; i++)
            {
                if (!newState.IsConnected) {
                    ButtonReleased[buttons[i]] = false;
                }
                else {
                    if (State.IsButtonDown(buttons[i]) && newState.IsButtonUp(buttons[i]))
                        ButtonReleased[buttons[i]] = true;
                    else
                        ButtonReleased[buttons[i]] = false;
                }
            }

            for (int i = 0; i < timedButtons.Length; i++)
            {
                TimedButtonTriggered[timedButtons[i]] = false;
            }

            State = newState;
        }

        private GamePadState newState;
        public void Update(GameTime gameTime) {
            if (GamePad.GetCapabilities(Index).GamePadType != GamePadType.GamePad)
            {
                State = new GamePadState();
                return;
            }
            newState = GamePad.GetState(Index);
            Update(ref newState);

            if (newState.IsConnected) {
                TestTimedButton(Buttons.LeftThumbstickLeft, Buttons.LeftThumbstickRight, newState.ThumbSticks.Left.X);
                TestTimedButton(Buttons.LeftThumbstickDown, Buttons.LeftThumbstickUp, newState.ThumbSticks.Left.Y);
                TestTimedButton(Buttons.RightThumbstickLeft, Buttons.RightThumbstickRight, newState.ThumbSticks.Right.X);
                TestTimedButton(Buttons.RightThumbstickDown, Buttons.RightThumbstickUp, newState.ThumbSticks.Right.Y);

                for (int i = 0; i < timedButtons.Length; i++) {
                    ButtonTimers[timedButtons[i]] -= gameTime.ElapsedGameTime.TotalSeconds;
                    if (ButtonTimers[timedButtons[i]] < 0) { ButtonTimers[timedButtons[i]] = 0; }
                }
            }
            if (vibrateTimer > 0) {
                if (vibrateElapsed < 0) {
                    GamePad.SetVibration(Index, 0, 0);
                }
                else if (vibrateElapsed < vibrateTimer) {
                    GamePad.SetVibration(Index, vibrateLeft, vibrateRight);
                }
                else {
                    GamePad.SetVibration(Index, 0, 0);
                    vibrateTimer = 0f;
                }

                vibrateElapsed += gameTime.ElapsedGameTime.TotalSeconds;
            }
        }

        public void VibratePulse(Double seconds, Double delay, float leftMotor, float rightMotor) {
            vibrateTimer = seconds;
            vibrateElapsed = 0 - delay;
            vibrateLeft = leftMotor;
            vibrateRight = rightMotor;
        }

        public void ClearVibrate() {
            vibrateElapsed = 0;
            vibrateTimer = 0;
            GamePad.SetVibration(Index, 0, 0);
        }

        protected void TestTimedButton(Buttons buttonNegative, Buttons buttonPositive, float axis) {
            if (axis > analogThreshold) {
                if (ButtonTimers[buttonPositive] == 0) {
                    TimedButtonTriggered[buttonPositive] = true;
                    ButtonTimers[buttonPositive] = timedThreshold;
                }
            }
            else {
                ButtonTimers[buttonPositive] = 0;
            }


            if (axis < (analogThreshold * -1)) {
                if (ButtonTimers[buttonNegative] == 0) {
                    TimedButtonTriggered[buttonNegative] = true;
                    ButtonTimers[buttonNegative] = timedThreshold;
                }
            }
            else {
                ButtonTimers[buttonNegative] = 0;
            }
        }
    }
}
